package dstree

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"math"
	"os"
	"strconv"
)

func (index *Dstree_Index) Get_nodeinfo(node *Dstree_Node) string {

	nodebyte := Dstree_node_write_to_byte(node)
	// fmt.Printf("    Get_nodeinfo lenth = %d\n", len(nodebyte))
	return string(nodebyte)
}

func Load_nodeinfo(nodeinfo []byte) (Dstree_Node, error) {

	return *Dstree_node_read_from_byte(nodeinfo), nil
}

func Dstree_node_write_to_byte(node *Dstree_Node) []byte {

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, node.Level)

	//存分裂策略
	binary.Write(bytesBuffer, binary.BigEndian, node.Split_Policy.Measure)

	binary.Write(bytesBuffer, binary.BigEndian, node.Split_Policy.Split_From_idx)

	binary.Write(bytesBuffer, binary.BigEndian, node.Split_Policy.Split_To_idx)

	bytes := make([]byte, 4)
	binary.LittleEndian.PutUint32(bytes, math.Float32bits(float32(node.Split_Policy.Indicator_Split_Value)))
	bytesBuffer.Write(bytes)

	binary.Write(bytesBuffer, binary.BigEndian, node.Is_leaf)
	binary.Write(bytesBuffer, binary.BigEndian, node.Is_left)
	binary.Write(bytesBuffer, binary.BigEndian, node.Node_Size)

	//存分段右端点
	// x = int32(node.Node_Points_num)
	binary.Write(bytesBuffer, binary.BigEndian, node.Node_Points_num)
	// fmt.Printf("               binary.Write node.Node_Points_num= %d\n", node.Node_Points_num)
	for _, p := range node.Node_Points {
		binary.Write(bytesBuffer, binary.BigEndian, p)
		// fmt.Printf("               binary.Write node.Node_Points %d\n", p)
	}
	for _, s := range node.Segment_Sketches {
		for i := 0; i < 4; i++ {
			bytes := make([]byte, 4)
			binary.LittleEndian.PutUint32(bytes, math.Float32bits(float32(s.Indicators[i])))
			bytesBuffer.Write(bytes)
		}
		// fmt.Printf("               binary.Write node.Segment_Sketches %v\n", s)

	}
	// x = int32(node.Hs_split_points_num)
	binary.Write(bytesBuffer, binary.BigEndian, node.Hs_split_points_num)
	// fmt.Printf("               binary.Write node.Hs_split_points_num %d\n", node.Hs_split_points_num)
	for _, p := range node.Hs_split_points {
		binary.Write(bytesBuffer, binary.BigEndian, p)
	}
	for _, s := range node.Hs_Segment_Sketches {
		for i := 0; i < 4; i++ {
			bytes := make([]byte, 4)
			binary.LittleEndian.PutUint32(bytes, math.Float32bits(float32(s.Indicators[i])))
			bytesBuffer.Write(bytes)
		}

	}
	// fmt.Printf("bytebuffer len=%d\n, byte %x\n", len(bytesBuffer.Bytes()), bytesBuffer.Bytes())
	return bytesBuffer.Bytes()
}

func Dstree_node_read_from_byte(bytedata []byte) *Dstree_Node {
	// fmt.Printf("               Dstree_node_read_from_byte lenth = %d\n , byte=%x\n", len(bytedata), bytedata)
	node := new(Dstree_Node)
	bytesBuffer := bytes.NewBuffer(bytedata)
	// var temp int32
	binary.Read(bytesBuffer, binary.BigEndian, &node.Level)

	//读分裂策略
	binary.Read(bytesBuffer, binary.BigEndian, &node.Split_Policy.Measure)
	// fmt.Printf("measure = %d  ", node.Split_Policy.Measure)
	binary.Read(bytesBuffer, binary.BigEndian, &node.Split_Policy.Split_From_idx)
	// fmt.Printf("Split_From_idx = %d  ", node.Split_Policy.Split_From_idx)
	binary.Read(bytesBuffer, binary.BigEndian, &node.Split_Policy.Split_To_idx)
	// fmt.Printf("Split_To_idx = %d  ", node.Split_Policy.Split_To_idx)
	bytes := make([]byte, 4)
	bytesBuffer.Read(bytes)
	node.Split_Policy.Indicator_Split_Value = Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bytes)))

	binary.Read(bytesBuffer, binary.BigEndian, &node.Is_leaf)
	binary.Read(bytesBuffer, binary.BigEndian, &node.Is_left)
	binary.Read(bytesBuffer, binary.BigEndian, &node.Node_Size)
	// fmt.Printf("              is leaf=%v\n", node.Is_leaf)
	// fmt.Printf("              is left=%v\n", node.Is_left)
	// fmt.Printf("              Node_Size=%v\n", node.Node_Size)
	//读分段右端点
	binary.Read(bytesBuffer, binary.BigEndian, &node.Node_Points_num)
	node.Node_Points = make([]int16, node.Node_Points_num)
	node.Segment_Sketches = make([]Segment_Sketch, node.Node_Points_num)
	var i int16
	for i = 0; i < node.Node_Points_num; i++ {
		// var x int16
		binary.Read(bytesBuffer, binary.BigEndian, &node.Node_Points[i])
		// node.Node_Points[i] = x
		// fmt.Printf("               node Node points %d = %d\n", i, node.Node_Points[i])

	}
	for i = 0; i < node.Node_Points_num; i++ {
		node.Segment_Sketches[i].Indicators = make([]Ts_type, 4)
		for j := 0; j < 4; j++ {
			bytes := make([]byte, 4)
			bytesBuffer.Read(bytes)
			node.Segment_Sketches[i].Indicators[j] = Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bytes)))
		}
		// fmt.Printf("               node Node points %d sketches = %v\n", i, node.Segment_Sketches[i])
	}

	binary.Read(bytesBuffer, binary.BigEndian, &node.Hs_split_points_num)
	// node.Hs_split_points_num = int(temp)
	// fmt.Printf("               Hs_split_points_num=%d\n", node.Hs_split_points_num)
	node.Hs_split_points = make([]int16, node.Hs_split_points_num)
	node.Hs_Segment_Sketches = make([]Segment_Sketch, node.Hs_split_points_num)
	for i = 0; i < node.Hs_split_points_num; i++ {
		var x int16
		binary.Read(bytesBuffer, binary.BigEndian, &x)
		node.Hs_split_points[i] = x

	}
	for i = 0; i < node.Hs_split_points_num; i++ {
		node.Hs_Segment_Sketches[i].Indicators = make([]Ts_type, 4)
		for j := 0; j < 4; j++ {
			bytes := make([]byte, 4)
			bytesBuffer.Read(bytes)
			node.Hs_Segment_Sketches[i].Indicators[j] = Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bytes)))
		}
	}

	return node
}

func Loadindex(filename string) (*Dstree_Index, error) {

	return nil, nil
}
func tsdataToHash(data []Ts_type) string {
	var byteData []byte
	for _, value := range data {
		bytes := make([]byte, 4)
		binary.LittleEndian.PutUint32(bytes, math.Float32bits(float32(value)))
		byteData = append(byteData, bytes...)
	}
	data_value_hash := sha256.New()
	// data_value_hash.Write([]byte(datajson))
	data_value_hash.Write(byteData)

	datahash := data_value_hash.Sum(nil)
	return string(datahash)
}

func convertToBytes(data [][]Ts_type) []byte {
	var byteData []byte
	for _, row := range data {
		for _, value := range row {
			bytes := make([]byte, 4)
			binary.LittleEndian.PutUint32(bytes, math.Float32bits(float32(value)))
			byteData = append(byteData, bytes...)
		}
	}
	return byteData
}

// row:多少条data
// clos：维度
func restoreFromString(strData string, rows, cols int) [][]Ts_type {
	var restoredData [][]Ts_type

	for i := 0; i < len(strData); i += 4 * cols {
		rowBytes := []byte(strData[i : i+4*cols])
		var row []Ts_type

		for j := 0; j < len(rowBytes); j += 4 {
			value := math.Float32frombits(binary.LittleEndian.Uint32(rowBytes[j : j+4]))
			row = append(row, Ts_type(value))
		}

		restoredData = append(restoredData, row)
	}

	return restoredData
}

// func IndexReadFromFile(filePath string) (*Dstree_Index, error) {
// 	index, err := LoadIndexFromJSON(filePath)
// 	if err != nil {
// 		return nil, err
// 	}
// 	records, err := LoadTimeseriesFromCSV(filePath)
// 	if err != nil {
// 		return nil, err
// 	}
// 	err = LoadDataFromCSV(index, index.FirstNode, records)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return index, nil
// }

// LoadBinaryTreeFromJSON loads a binary tree from a JSON file
func LoadIndexFromJSON(filePath string) (*Dstree_Index, error) {
	filename := filePath + "index.json"
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var index Dstree_Index
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&index)
	if err != nil {
		return nil, err
	}

	return &index, nil
}

// func LoadDataFromCSV(index *Dstree_Index, node *Dstree_Node, records [][]string) error {
// 	if node.Is_leaf {
// 		//filename := filePath + node.Name
// 		tsdata, err := Leaf_node_load_data(node.DataStart, node.DataStart+int(node.Node_Size), records)
// 		if err != nil {
// 			return err
// 		}
// 		node.data = &tsdata
// 		index.data = append(index.data, &tsdata)
// 	} else {
// 		err := LoadDataFromCSV(index, node.Left_child, records)
// 		if err != nil {
// 			return err
// 		}
// 		err = LoadDataFromCSV(index, node.Right_child, records)
// 		if err != nil {
// 			return err
// 		}
// 	}
// 	return nil
// }

func Leaf_node_load_data(from, to int, records [][]string) (Timeseries, error) {
	var data Timeseries
	for idx := from; idx < to; idx++ {
		row := make([]Ts_type, len(records[idx]))
		for i, valueStr := range records[idx] {
			value, err := strconv.ParseFloat(valueStr, 32)
			if err != nil {
				return nil, err
			}
			row[i] = Ts_type(value)
		}
		data = append(data, row)
	}
	return data, nil
}

// func WriteIndexDatatoCSV(filePath string, index *Dstree_Index, node *Dstree_Node) error {
// 	filename := filePath + "data.csv"
// 	file, err := os.Create(filename)
// 	if err != nil {
// 		return err
// 	}
// 	defer file.Close()

// 	writer := csv.NewWriter(file)
// 	defer writer.Flush()
// 	start := 0
// 	err = SaveDatatoCSV(writer, node, &start)
// 	if err != nil {
// 		return err
// 	}
// 	return nil
// }

func IndexWritetoFile(filePath string, index *Dstree_Index, node *Dstree_Node) error {

	err := SaveIndexToJSON(filePath, index)
	if err != nil {
		return err
	}
	return nil
}

func SaveIndexToJSON(filename string, index *Dstree_Index) error {
	filename = filename + "index.json"
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	err = encoder.Encode(index)
	if err != nil {
		return err
	}

	return nil
}

// func SaveDatatoCSV(writer *csv.Writer, node *Dstree_Node, start *int) error {
// 	//叶节点
// 	if node.Is_leaf {
// 		node.DataStart = *start
// 		*start = *start + int(node.Node_Size)
// 		return SaveTimeseriesToCSV(writer, *node.data)
// 	} else {
// 		err := SaveDatatoCSV(writer, node.Left_child, start)
// 		if err != nil {
// 			return err
// 		}
// 		err = SaveDatatoCSV(writer, node.Right_child, start)
// 		if err != nil {
// 			return err
// 		}
// 	}
// 	return nil
// }

func SaveTimeseriesToCSV(writer *csv.Writer, data Timeseries) error {

	for _, row := range data {
		strRow := make([]string, len(row))
		for i, value := range row {
			strRow[i] = strconv.FormatFloat(float64(value), 'f', -1, 32)
		}
		if err := writer.Write(strRow); err != nil {
			return err
		}
	}

	return nil
}

// LoadTimeseriesFromCSV 从 CSV 文件加载 Timeseries
func LoadTimeseriesFromCSV(filename string) ([][]string, error) {
	filename = filename + "data.csv"
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		return nil, err
	}
	return records, nil
}

func (index *Dstree_Index) InsertTimeseriseFromCSV(filename string) (int, error) {

	file, err := os.Open(filename)
	if err != nil {
		return 0, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		return 0, err
	}

	//var data Timeseries
	n := 0
	for _, record := range records {
		row := make([]Ts_type, len(record))
		for i, valueStr := range record {
			value, err := strconv.ParseFloat(valueStr, 32)
			if err != nil {
				return 0, err
			}
			row[i] = Ts_type(value)
		}
		index.insert(row)
		n++
	}

	return n, nil
}

func LoadDataFromBin(location string, dataSize int, datalenth int) [][]Ts_type {
	f, err := os.Open(location)
	if err != nil {
		panic("couldn't open data file")
	}
	defer f.Close()
	count := 1
	datas := make([][]Ts_type, 0)
	for count <= dataSize {
		vec := make([]Ts_type, datalenth)
		for i := 0; i < 320; i++ {
			bs := make([]byte, 4)
			f.Read(bs)
			if i < datalenth {
				vec[i] = Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bs)))
			}

			// cycle := 320 / datalenth
			// var v Ts_type = 0
			// for j := 1; j < cycle; j++ {
			// 	bs := make([]byte, 4)
			// 	f.Read(bs)
			// 	// f.Read(bs1)
			// 	v += Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bs)))
			// }

			// bs1 := make([]byte, 4)

			// bs2 := make([]byte, 4)
			// f.Read(bs2)
			// v += Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bs2)))
			// bs3 := make([]byte, 4)
			// v += Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bs3)))
			// bs4 := make([]byte, 4)
			// v += Ts_type(math.Float32frombits(binary.LittleEndian.Uint32(bs4)))

			// vec[i] = v / Ts_type(cycle)
		}
		datas = append(datas, vec)
		count++
		if count%1000000 == 0 {
			fmt.Printf("Read %v records\n", count)
		}
	}
	return datas
}
